package com.atguigu.gulimall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.common.model.SkuEsModel;
import com.atguigu.common.utils.R;
import com.atguigu.gulimall.search.config.ElasticSearchConfig;
import com.atguigu.gulimall.search.constant.EsConstant;
import com.atguigu.gulimall.search.feign.ProductService;
import com.atguigu.gulimall.search.model.vo.AttrRespVO;
import com.atguigu.gulimall.search.model.vo.SearchParam;
import com.atguigu.gulimall.search.model.vo.SearchResult;
import com.atguigu.gulimall.search.service.MallSearchService;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.TotalHits;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class MallSearchServiceImpl implements MallSearchService {
    @Autowired
    private RestHighLevelClient client;
    @Autowired
    private ProductService productService;

    @Override
    public SearchResult search(SearchParam searchParam) {
        //1.准备检索请求
        SearchRequest searchRequest = this.buildSearchRequest(searchParam);
        SearchResult result = null;
        try {
            //2.执行检索请求
            SearchResponse searchResponse = client.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);
            //3.封装响应数据为需要的格式
            result = this.buildSearchResult(searchResponse, searchParam);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    private SearchResult buildSearchResult(SearchResponse searchResponse, SearchParam searchParam) {
        SearchResult searchResult = new SearchResult();
        //1、返回的所有查询到的商品
        SearchHits searchHits = searchResponse.getHits();
        SearchHit[] searchHitArr = searchHits.getHits();
        List<SkuEsModel> skuEsModelList = new ArrayList<>();
        for (SearchHit searchHit : searchHitArr) {
            String sourceAsString = searchHit.getSourceAsString();
            SkuEsModel skuEsModel = JSON.parseObject(sourceAsString, SkuEsModel.class);

            if (StringUtils.isNotEmpty(searchParam.getKeyword())) {
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                HighlightField skuTitle = highlightFields.get("skuTitle");
                skuEsModel.setSkuTitle(skuTitle.getFragments()[0].string());
            }

            skuEsModelList.add(skuEsModel);
        }
        if (skuEsModelList.size() > 1) {
            searchResult.setProducts(skuEsModelList);
        }
        //================聚合里拼装================
        Aggregations aggregations = searchResponse.getAggregations();
        //2、当前商品设计到的所有属性信息
        ParsedNested attrAgg = aggregations.get("attrAgg");
        List<SearchResult.Attrs> attrsList = new ArrayList<>();
        ParsedLongTerms attrIdTerms = attrAgg.getAggregations().get("attrIdAgg");
        for (Terms.Bucket bucket : attrIdTerms.getBuckets()) {
            Long attrId = bucket.getKeyAsNumber().longValue();

            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedStringTerms attrNameTerms = bucketAggregations.get("attrNameAgg");
            String attrName = attrNameTerms.getBuckets().get(0).getKeyAsString();

            ParsedStringTerms attrValueTerms = bucketAggregations.get("attrValueAgg");
            List<String> attrValueList = new ArrayList<>();
            if (attrValueTerms != null && !CollectionUtils.isEmpty(attrValueTerms.getBuckets()))
                for (Terms.Bucket attrValueTermsBucket : attrValueTerms.getBuckets()) {
                    String attrValue = attrValueTermsBucket.getKeyAsString();
                    if (StringUtils.isNotEmpty(attrValue) && attrValue.contains(";")) {
                        for (String value : attrValue.split(";")) {
                            attrValueList.add(value);
                        }
                    } else {
                        attrValueList.add(attrValue);
                    }
                }

            SearchResult.Attrs attrs = new SearchResult.Attrs();
            attrs.setAttrId(attrId);
            attrs.setAttrName(attrName);
            List<String> collect = attrValueList.stream().distinct().collect(Collectors.toList());

            List<String> checkedAttrValue = new ArrayList<>();
            List<String> attrParam = searchParam.getAttrs();
            if (!CollectionUtils.isEmpty(attrParam))
                for (String attr : attrParam) {
                    String[] idAndValues = attr.split("_");
                    String[] values = idAndValues[1].split(":");
                    for (String value : values) {
                        checkedAttrValue.add(value);
                    }
                }

            collect.removeAll(checkedAttrValue);
            if (collect.size() > 1) {
                attrs.setAttrValue(collect);
                attrsList.add(attrs);
            }
        }
        searchResult.setAttrs(attrsList);
        //3、当前商品设计到的所有品牌信息
        ParsedLongTerms brandTerms = aggregations.get("brandAgg");
        List<SearchResult.BrandVO> brandVOList = new ArrayList<>();
        if (brandTerms != null && !CollectionUtils.isEmpty(brandTerms.getBuckets()))
            for (Terms.Bucket bucket : brandTerms.getBuckets()) {
                Long brandId = bucket.getKeyAsNumber().longValue();

                Aggregations bucketAggregations = bucket.getAggregations();
                ParsedStringTerms brandNameTerms = bucketAggregations.get("brandNameAgg");
                String brandName = brandNameTerms.getBuckets().get(0).getKeyAsString();
                ParsedStringTerms brandImgTerms = bucketAggregations.get("brandImgAgg");
                String brandImg = brandImgTerms.getBuckets().get(0).getKeyAsString();

                SearchResult.BrandVO brandVO = new SearchResult.BrandVO();
                brandVO.setBrandId(brandId);
                brandVO.setBrandName(brandName);
                brandVO.setBrandImg(brandImg);
                brandVOList.add(brandVO);
            }
        if (brandVOList.size() > 1) {
            searchResult.setBrands(brandVOList);
        }
        //4、当前商品设计到的所有分类信息
        ParsedLongTerms catalogTerms = aggregations.get("catalogAgg");
        List<SearchResult.CatalogVO> catalogVOList = new ArrayList<>();
        if (catalogTerms != null && !CollectionUtils.isEmpty(catalogTerms.getBuckets()))
            for (Terms.Bucket bucket : catalogTerms.getBuckets()) {
                Long catalogId = bucket.getKeyAsNumber().longValue();

                ParsedStringTerms catalogNameTerms = bucket.getAggregations().get("catalogNameAgg");
                String catalogName = (String) catalogNameTerms.getBuckets().get(0).getKey();

                SearchResult.CatalogVO catalogVO = new SearchResult.CatalogVO();
                catalogVO.setCatalogId(catalogId);
                catalogVO.setCatalogName(catalogName);
                catalogVOList.add(catalogVO);
            }
        if (catalogVOList.size() > 1) {
            searchResult.setCatalogs(catalogVOList);
        }
        //5、分页信息(页码、总记录数)
        TotalHits totalHits = searchHits.getTotalHits();
        if (totalHits != null) {
            searchResult.setTotal(totalHits.value);
            searchResult.setPageNum(searchParam.getPageNum());
            searchResult.setTotalPages(Math.toIntExact(totalHits.value % EsConstant.PRODUCT_PAGE_SIZE == 0 ? totalHits.value / EsConstant.PRODUCT_PAGE_SIZE : totalHits.value / EsConstant.PRODUCT_PAGE_SIZE + 1));

            List<Integer> pageNavs = new ArrayList<>();
            for (int i = 1; i <= searchResult.getTotalPages(); i++) {
                pageNavs.add(i);
            }
            searchResult.setPageNavs(pageNavs);
        }
        //6、构建面包屑导航功能
        List<String> attrs = searchParam.getAttrs();
        if (!CollectionUtils.isEmpty(attrs)) {
            List<SearchResult.NavVO> navVOS = attrs.stream().map(attr -> {
                //attrs=2_5寸:6寸
                SearchResult.NavVO navVO = new SearchResult.NavVO();

                String[] attrNameAndValues = attr.split("_");
                String attrId = attrNameAndValues[0];
                R attrInfo = productService.attrInfo(Long.valueOf(attrId));
                String attrInfoJson = JSON.toJSONString(attrInfo.get("attr"));
                AttrRespVO attrRespVO = JSON.parseObject(attrInfoJson, AttrRespVO.class);

                navVO.setNavName(attrRespVO.getAttrName());
                navVO.setNavValue(attrNameAndValues[1]);
                //拿到所有的查询条件
                try {
                    String encode = URLEncoder.encode(attr, "UTF-8");
                    String replace = searchParam.getNativeQueryString().replace("?attrs=" + encode, "").replace("&attrs=" + encode, "").replace("attrs=" + encode, "");
                    if ("".endsWith(replace)) {
                        navVO.setLink("http://search.gulimall.com/list.html" + replace);
                    } else {
                        navVO.setLink("http://search.gulimall.com/list.html?" + replace);
                    }
                } catch (UnsupportedEncodingException e) {
                    e.printStackTrace();
                }

                return navVO;
            }).collect(Collectors.toList());
            searchResult.setNavs(navVOS);
        }
        return searchResult;
    }

    /**
     * 构建请求
     *
     * @param searchParam
     * @return
     */
    private SearchRequest buildSearchRequest(SearchParam searchParam) {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        /**
         * 模糊匹配、过滤（属性、分类、品牌、价格区间、库存）
         */
        //构建 bool-query
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //1.1 must 模糊查询
        if (StringUtils.isNotEmpty(searchParam.getKeyword())) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("skuTitle", searchParam.getKeyword()));
        }
        //1.2 filter
        //按照三级分类id查询
        if (searchParam.getCatalog3Id() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("catalogId", searchParam.getCatalog3Id()));
        }
        //按照品牌id查询
        if (!CollectionUtils.isEmpty(searchParam.getBrandId())) {
            boolQueryBuilder.filter(QueryBuilders.termsQuery("brandId", searchParam.getBrandId()));
        }
        //按照是否拥有库存查询
        if (searchParam.getHasStock() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("hasStock", searchParam.getHasStock() == 1));
        }
        //按照价格区间查询
        if (StringUtils.isNotEmpty(searchParam.getSkuPrice())) {
            String skuPrice = searchParam.getSkuPrice();

            if (skuPrice.endsWith("_")) {
                skuPrice += Integer.MAX_VALUE;
            }
            if (skuPrice.startsWith("_")) {
                skuPrice = Integer.MIN_VALUE + skuPrice;
            }

            String[] skuPriceList = skuPrice.split("_");

            String minPrice = skuPriceList[0];
            String maxPrice = skuPriceList[1];

            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("skuPrice");
            if (StringUtils.isNotEmpty(minPrice)) {
                rangeQueryBuilder.gte(minPrice);
            }
            if (StringUtils.isNotEmpty(maxPrice)) {
                rangeQueryBuilder.lte(maxPrice);
            }
            boolQueryBuilder.filter(rangeQueryBuilder);
        }
        //按照属性进行查询
        if (!CollectionUtils.isEmpty(searchParam.getAttrs())) {
            //attrs=1_5寸:8寸&attrs=2_3G:4G:5G
            for (String attr : searchParam.getAttrs()) {
                BoolQueryBuilder nestedBoolQueryBuilder = QueryBuilders.boolQuery();

                String[] attrIdAndValuesStr = attr.split("_");
                String attrId = attrIdAndValuesStr[0];

                String attrValuesStr = attrIdAndValuesStr[1];
                String[] attrValues = attrValuesStr.split(":");

                nestedBoolQueryBuilder.must(QueryBuilders.termQuery("attrs.attrId", attrId));
                nestedBoolQueryBuilder.must(QueryBuilders.termsQuery("attrs.attrValue", attrValues));
                //每一个必须都得生成一个nested查询
                NestedQueryBuilder nestedQueryBuilder = QueryBuilders.nestedQuery("attrs", nestedBoolQueryBuilder, ScoreMode.None);
                boolQueryBuilder.filter(nestedQueryBuilder);
            }
        }
        sourceBuilder.query(boolQueryBuilder);
        /**
         * 排序、分页、高亮
         */
        //2.1 排序
        if (StringUtils.isNotEmpty(searchParam.getSort())) {
            String sortStr = searchParam.getSort();
            String[] sortNameAndType = sortStr.split("_");

            String sortName = sortNameAndType[0];
            String sortType = sortNameAndType[1];

            sourceBuilder.sort(sortName, "asc".equals(sortType) ? SortOrder.ASC : SortOrder.DESC);
        }
        //2.2 分页
        sourceBuilder.from((searchParam.getPageNum() - 1) * EsConstant.PRODUCT_PAGE_SIZE);
        sourceBuilder.size(EsConstant.PRODUCT_PAGE_SIZE);
        //2.3 高亮
        if (StringUtils.isNotEmpty(searchParam.getKeyword())) {
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("skuTitle");
            highlightBuilder.preTags("<b style='color:red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }
        /**
         * 聚合分析
         */
        //1 品牌聚合
        //1.1 品牌id聚合
        TermsAggregationBuilder brandAgg = AggregationBuilders.terms("brandAgg").field("brandId").size(10);
        //1.2 品牌名称子聚合
        TermsAggregationBuilder brandNameAgg = AggregationBuilders.terms("brandNameAgg").field("brandName").size(1);
        brandAgg.subAggregation(brandNameAgg);
        //1.3 品牌图片子聚合
        TermsAggregationBuilder brandImgAgg = AggregationBuilders.terms("brandImgAgg").field("brandImg").size(1);
        brandAgg.subAggregation(brandImgAgg);
        sourceBuilder.aggregation(brandAgg);
        //2 分类聚合
        //2.1 分类id聚合
        TermsAggregationBuilder catalogAgg = AggregationBuilders.terms("catalogAgg").field("catalogId").size(10);
        //2.2 分类名称子聚合
        TermsAggregationBuilder catalogNameAgg = AggregationBuilders.terms("catalogNameAgg").field("catalogName").size(1);
        catalogAgg.subAggregation(catalogNameAgg);
        sourceBuilder.aggregation(catalogAgg);
        //3 属性聚合
        //3.1 属性嵌入式聚合
        NestedAggregationBuilder attrsNestedAgg = AggregationBuilders.nested("attrAgg", "attrs");
        //3.2 属性id聚合
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").size(10);
        //3.3 属性名称子聚合
        TermsAggregationBuilder attrNameAgg = AggregationBuilders.terms("attrNameAgg").field("attrs.attrName").size(1);
        TermsAggregationBuilder attrValueAgg = AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue").size(10);
        attrIdAgg.subAggregation(attrNameAgg);
        attrIdAgg.subAggregation(attrValueAgg);
        attrsNestedAgg.subAggregation(attrIdAgg);

        sourceBuilder.aggregation(attrsNestedAgg);

        System.out.println(sourceBuilder);
        return new SearchRequest(new String[]{EsConstant.PRODUCT_INDEX}, sourceBuilder);
    }

}
